from django.apps import apps
from django.db import models
from django.urls import clear_url_caches, get_resolver, URLResolver

from super.dynamic.core.fields import FIELD_MAPPING
from super.system.routes import router


class DynamicLoader:
    """
    动态加载器
    """
    memoryCache = {}
    columnsExcludeType = ['divider', 'static-text', 'grid-col', 'html-text', 'button', 'table']

    def __init__(self, *args, table_prefix='dynamictable', **kwargs):
        self.table_prefix = table_prefix

    def load(self):
        try:
            dynamics = apps.all_models['system'].get('menu').objects.filter(is_dynamic=True).exclude(is_catalog=True)

            for d in dynamics:
                self.load_table(d)
        except Exception:
            pass

    def load_table(self, table, runtime=False):
        self.remove_model(table)
        self.load_model(table)
        self.load_serializer(table)
        self.load_view_set(table, runtime)

    def remove_model(self, table, key=None):
        key = key or self.get_key(table.code).lower()
        if apps.all_models['dynamic'].get(key):
            apps.all_models['dynamic'].pop(key)

    def load_model(self, table):
        attrs = self.transform(table.form_design.get('widgetList'))

        class Meta:
            verbose_name = table.name
            verbose_name_plural = verbose_name

        attrs['Meta'] = Meta
        attrs['__module__'] = 'super.dynamic.models'
        from super.utils.models import CoreModel
        key = self.get_key(table.code)
        model_cls = type(key, (CoreModel,), attrs)
        if not self.memoryCache.get(key, {}):
            self.memoryCache.setdefault(key, {})
        cache = self.memoryCache.get(key, {})
        cache.update({'model': model_cls})

    def load_serializer(self, table):
        key = self.get_key(table.code)
        cache = self.memoryCache.get(key, {})

        class Meta:
            model = cache.get('model')
            fields = '__all__'

        from super.utils.serializers import CustomModelSerializer
        serializer = type(f'{key}Serializer', (CustomModelSerializer,), {
            'Meta': Meta,
            '__package__': 'super.dynamic.serializers',
            '__module__': 'super.dynamic.serializers',
        })
        cache.update({'serializer': serializer})

    def load_view_set(self, table, runtime=False):
        key = self.get_key(table.code)
        cache = self.memoryCache.get(key, {})
        model_cls = cache.get('model')
        serializer_cls = cache.get('serializer')
        attrs = {
            'queryset': model_cls.objects.order_by('-create_datetime'),
            # 'filter_fields': self.get_filter_fields(model_cls),
            'serializer_class': serializer_cls,
            '__module__': 'super.dynamic.viewsets'
        }

        from super.utils.viewset import CustomModelViewSet
        viewset = type(f'{key}ViewSet', (CustomModelViewSet,), attrs)

        if runtime:
            self.clear_url(table, router.load_url(key.lower(), viewset))
        else:
            router.register(key.lower(), viewset)

    @staticmethod
    def get_filter_fields(model_cls):
        fields = {}
        for f in model_cls._meta.fields:
            lookup = ''
            if isinstance(f, models.JSONField):
                lookup = ('contains',)
            elif isinstance(f, models.CharField):
                lookup = ('exact',)
            if lookup:
                fields[f.name] = lookup
        return fields

    @staticmethod
    def get_key(name: str):
        unique = 'dynamic_table'
        return f"""{unique[0].upper()}{unique[1:]}{name}"""

    def transform(self, fields: list, attrs=None):
        # print(field_props)
        # fields = field_props.get('widgetList', [])
        attrs = attrs or {}
        for f in fields:
            if f.get('widgetList') and len(f.get('widgetList')) > 0:
                attrs.update(self.transform(f.get('widgetList'), attrs))
            elif f.get('tabs') and len(f.get('tabs')) > 0:
                attrs.update(self.transform(f.get('tabs'), attrs))
            elif f.get('cols') and len(f.get('cols')) > 0:
                attrs.update(self.transform(f.get('cols'), attrs))
            elif f.get('type') and f.get('type') not in self.columnsExcludeType:
                # print(f.get('type'), f)
                dj_field = FIELD_MAPPING[f['type']](f.get('options'), field_type=f['type']).to_python()
                attrs[f.get('id')] = dj_field
        return attrs

    def clear_url(self, instance, urlpatterns=None):
        key = self.get_key(instance.name).lower()
        if urlpatterns is None:
            router.cancel(key)
            self.remove_model(instance)
        self.reload_urls(key, urlpatterns)
        clear_url_caches()

    @staticmethod
    def reload_urls(key, urlpatterns=None):
        urlpatterns = urlpatterns or []
        url_patterns = get_resolver().url_patterns
        for resolver in url_patterns:
            if isinstance(resolver, URLResolver):
                for index in range(len(resolver.url_patterns) - 1, -1, -1):
                    pattern = resolver.url_patterns[index]
                    if pattern.pattern and pattern.pattern.name and key in pattern.pattern.name:
                        del resolver.url_patterns[index]
                if urlpatterns and resolver.app_name == 'super.system':
                    resolver.url_patterns.extend(urlpatterns)


loader = DynamicLoader()
